/*	
 *				
 *					ISNW ---- It is a war.It is not warcraft!
 *
 *
 *					Author:					zhoukeli(haxixi_keli)
 *					Filename:				isnwString.h
 *					CreateTime:				2008/12/06
 *					ModifyTime:				2008/12/06
 *					ModifyTime:				2008/12/16
 *
 */

#ifndef __ISNW_STRING_H__
#define __ISNW_STRING_H__

#include "isnwTypes.h"
#include "isnwArray.h"
#include "isnwAllocator.h"
#include "isnwMath.h"

namespace ISNW
{
namespace core
{
	template<typename T,typename TAlloc = isnwAllocator<T> >
	class stringbase
	{
	public:
		//! default constructor
		stringbase(){defaultconstruct();}

		//! copy construct
		stringbase(const stringbase<T>& other)
		{
			defaultconstruct();
			*this = other;
		}

		//! construct from a unicode or an ascii string
		stringbase(const T* str) 
		{
			defaultconstruct();
			*this = str;
		}

		//! construct from a double/float number
		stringbase(const f64 num)
		{
			defaultconstruct();

			*this = num;
		}

		stringbase(const f32 num)
		{
			defaultconstruct();

			*this = num;
		}

		//! construct from a int/unsigned int number
		stringbase(const s32 num)
		{
			defaultconstruct();

			*this = num;
		}

		stringbase(const u32 num)
		{
			defaultconstruct();

			*this = num;
		}

	public:
		//! operator
		bool operator == (T* p) const
		{
			for(u32 i = 0; i < used; ++i)
			{
				if(data[i] != p[i]) return false;
			}

			return true;
		}

		bool operator == (const stringbase<T>& other) const
		{
			return (*this == other.data);
		}

		//! copy operator
		stringbase<T>& operator += (const stringbase<T>& other)
		{
			u32 old_used = used;

			if(used + other.used > allocated)
				reallocate((used + other.used)*2);

			memcpy(data+used-1,other.data,other.used);
			used += other.used - 1;

			return *this;
		}

		stringbase<T>& operator += (const T* str)
		{
			u32 len = 0;
			const T* p = str;
			// calculate length
			while(*p++)++len;
			// allocate new memory
			u32 old_used = used;
			if(used+len > allocated)
				reallocate((used+len)*2);

			used += len;
			
			memcpy(data+old_used-1,str,len*sizeof(T));

			return *this;
		}

		stringbase<T>& operator += (const T ch)
		{
			T buf[2];
			buf[0] = ch;
			buf[1] = (T)0;

			*this += buf;

			return *this;
		}

		stringbase<T>& operator += (const f64 num)
		{
			T buf[256];
			memset(buf,0,sizeof(buf));
			if(sizeof(T) == sizeof(c8))
				sprintf_s(buf,"%0.16f",num);
			else
				swprintf_s(buf,L"%0.16f",num);

			*this += buf;

			return *this;
		}

		stringbase<T>& operator += (const f32 num)
		{
			T buf[16];
			memset(buf,0,sizeof(buf));
			if(sizeof(T) == sizeof(c8))
				sprintf_s(buf,"%0.6f",num);
			else
				swprintf_s(buf,L"%0.6f",num);

			*this += buf;

			return *this;
		}

		stringbase<T>& operator += (const s32 number)
		{
			bool s = false;
			s32 num = number;
			if(num < 0)
			{
				s = true;
				num *= -1;
			}

			T buf[16];
			memset(buf,0,sizeof(buf));
			
			u32 idx = 15;
			T zero = (T)'0';
			while(num)
			{
				u32 n = num % 10;
				num /= 10;
				buf[--idx] = zero + n; 
			}
			if(s)
				buf[--idx] = (T)'-';

			*this += buf + idx;

			return *this;
		}

		stringbase<T>& operator += (const u32 num)
		{
			*this += s32(num);
		}

		stringbase<T>& operator = (const T* p)
		{
			if(used > 1) reset();

			*this += p;

			return *this;
		}

		stringbase<T> operator + (const T* p) const
		{
			stringbase<T> ret = *this;
			ret += p;

			return ret;
		}

		stringbase<T> operator + (const stringbase<T>& other) const
		{
			stringbase<T> ret = *this;
			ret += other;

			return ret;
		}

		stringbase<T>& operator = (const stringbase<T>& other)
		{
			reset();
			*this += other;

			return *this;
		}

		T& operator [] (const u32 idx)
		{
			if(idx < used)
			{
				return data[idx];
			}

			return data[0];
		}

	public:
		s32 compare(const stringbase<T>& other) const
		{
			for(u32 i = 0; i < used; ++i)
			{
				if(data[i] < other.data[i]) return -1;
				if(data[i] > other.data[i]) return 1;
			}

			return 0;
		}
		
		// ignore case
		s32 icompare(const stringbase<T>& other) const
		{
			stringbase<T> op1 = *this;
			op1.tolowcase();

			stringbase<T> op2 = other;
			op2.tolowcase();

			return op1.compare(op2);
		}

		void tolowcase()
		{
			for(u32 i = 0; i < used; ++i)
			{
				if(data[i] >= 0x41 && data[i] <= 0x5A) data[i] += 0x20;
			}
		}

		stringbase<T> getlowcase() const
		{
			stringbase<T> ret(data);
			ret.tolowcase();

			return ret;
		}

		void toupcase()
		{
			for(u32 i = 0; i < used; ++i)
			{
				if(data[i] >= 0x61 && data[i] <= 0x7A) data[i] -= 0x20;
			}
		}

		stringbase<T> getupcase() const
		{
			stringbase<T> ret(data);
			ret.toupcase();

			return ret;
		}

		u32 replace(T o,T n)
		{
			u32 num = 0;
			for(u32 i = 0; i < used; ++i)
			{
				if(data[i] == o)
				{
					data[i] = n;
					num ++;
				}
			}

			return num;
		}

		s32 search(T c)
		{
			s32 pos = -1;
			for(u32 i = 0; i < used; ++i)
			{
				if(data[i] == c)
				{
					break;
				}
			}

			if(i < used) pos = i;

			return pos;
		}

		s32 reverse_search(T c)
		{
			s32 pos = used - 1;
			while(pos >= 0)
			{
				if(data[pos] == c) break;
				--pos;
			}

			return pos;
		}

		s32 erase(u32 pos,u32 len)
		{
			if(pos >= used) return 0;

			if(pos + len > used)
			{
				len = used - pos;	
			}

			u32 n_pos = pos + len;

			for(u32 i = 0; i < len-1; ++i)
			{
				allocator.destruct(&data[pos+i]);
				allocator.construct(&data[pos+i],data[n_pos+i]);

				if(n_pos+i >= used-1) break;
			}

			for(u32 i = 0; i < len; ++i)
			{
				allocator.destruct(&data[n_pos+i]);

				if(n_pos+i >= used-1) break;
			}

			used -= len;

			data[used-1] = (T)0;

			return len;
		}

		T* c_str() const {return data;}

		u32 size() const {return used-1;}

	protected:
		void defaultconstruct()
		{
			used = allocated = 1;
			data = allocator.allocate(1);
			data[0] = (T)0;
		}

		void reset()
		{
			allocator.deallocate(data);
			data = allocator.allocate(1);
			data[0] = (T)0;
			used = allocated = 1;
		}

		void reallocate(u32 cnt)
		{
			// size must bigger than 0
			if(cnt == 0) return;

			T* old_data = data;

			used = _min_<u32>(cnt,used);

			allocated = cnt;

			data = allocator.allocate(cnt);
			memset(data,0,cnt*sizeof(T));

			for(u32 i = 0; i < used; ++i)
			{
				data[i] = old_data[i];
			}

			allocator.deallocate(old_data);
		}


	protected:
		T*				data;
		u32				used;
		u32				allocated;
		TAlloc			allocator;
	};

	//! typedef for ascii string
	typedef stringbase<char> string;
	//! typedef for unicode string
	typedef stringbase<wchar_t> wstring;

} // end namespace core
} // end namespace ISNW
#endif